// 
// SoapTrace.Net - A soap message monitoring utility.
// 
// Copyright (C) 2004-2009 Matthew Ward
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
// 
// Matthew Ward (mrward@users.sourceforge.net)
// 

using SoapTrace.Core;
using SoapTrace.Core.Communication;
using SoapTrace.Core.Formatters;
using System;
using System.Diagnostics;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Forms;
using System.Data;

namespace SoapTrace
{
	/// <summary>
	/// The Soap Trace application's main form.
	/// </summary>
	public partial class MainForm : Form
	{
		/// <summary>
		/// A reference to the soap trace application object.
		/// </summary>
		private SoapTraceApplication m_App;
		
		/// <summary>
		/// Append item method's delegate so the method can be
		/// called from another thread.
		/// </summary>
		private delegate void AppendItemInvoker( SoapRequestAndResponse item );
		
		/// <summary>
		/// The caption displayed in the title bar of the main form.
		/// </summary>
		private string m_Caption = String.Empty;
		
		/// <summary>
		/// The current window state.
		/// </summary>
		private FormWindowState m_State = FormWindowState.Normal;
		
		/// <summary>
		/// The text to search for.
		/// </summary>
		private string m_FindText = String.Empty;
		
		/// <summary>
		/// A flag indicating whether the search should be case sensitive.
		/// </summary>
		private bool m_CaseSensitive;
		
		/// <summary>
		/// The currently active find text form dialog.
		/// </summary>
		private FindTextForm m_FindTextForm;
		
		/// <summary>
		/// The initial top position of the find text form relative
		/// to the main form.
		/// </summary>
		private const int InitialFindTextFormTop = 60;
		
		/// <summary>
		/// The initial left position of the find text form relative
		/// to the main form.
		/// </summary>
		private const int InitialFindTextFormLeft = 50;
		
		/// <summary>
		/// Delegate to be used from another thread when an error
		/// message needs to be displayed.
		/// </summary>
		private delegate void ShowErrorInvoker( string message );
		
		/// <summary>
		/// Flag indicating that the form should be closed.  This is
		/// used in the MainFormClosing event handler so we can determine
		/// whether we are really closing the main form because
		/// we are exiting the program or just hiding it and leaving
		/// the app running in the system tray.
		/// </summary>
		private bool m_Close;
		
		/// <summary>
		/// Method delegate used to redisplay the main form
		/// from another thread.
		/// </summary>
		private delegate void RedisplayInvoker( );
		
		/// <summary>
		/// The current form's size when it is in the Normal state.
		/// </summary>
		private Size m_CurrentClientSize;
		
		/// <summary>
		/// The current form's location when it is in the Normal state.
		/// </summary>
		private Point m_CurrentLocation;
		
		/// <summary>
		/// The previously active view.
		/// </summary>
		private SoapMessagesView m_PreviouslyActiveView;
		
		/// <summary>
		/// The tab page that was right clicked.
		/// </summary>
		/// <remarks>
		/// This is set after the tab control receives the mouse up event after
		/// the user lets go of the right click button.  The tab page is saved
		/// so that the main form can determine which page was right clicked
		/// after the user selects the "Close Tab" option from the tab control's
		/// context menu.
		/// </remarks>
		private TabPage RightClickedTabPage;
		
		/// <summary>
		/// Windows message indicating that the session is ending.
		/// </summary>
		private int WM_QUERYENDSESSION = 0x11;
		
		/// <summary>
		/// Flag indicating if the app should display itself in the
		/// system tray.</summary>
		private bool m_ShowInTray;
		
		/// <summary>
		/// Creates a new instance of the main form.
		/// </summary>
		/// <param name="app">The soap trace application associated
		/// with this form.</param>
		public MainForm( SoapTraceApplication app )
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent( );
			
			// Save reference to the app.
			m_App = app;
			
			// Set initial size and location.
			m_CurrentClientSize = ClientSize;
			m_CurrentLocation = Location;
            RichTextBox.CheckForIllegalCrossThreadCalls = false;
			// Initially active view.
			m_PreviouslyActiveView = SoapMessagesView;
		}
		
		/// <summary>
		/// Gets or sets the main form's configuration.
		/// </summary>
		public MainFormConfiguration Configuration
		{
			get
			{
				return GetConfiguration( );
			}
			
			set
			{
				UpdateMainForm( value );
			}
		}
		
		/// <summary>
		/// Gets the active soap messages view.
		/// </summary>
		public SoapMessagesView ActiveView
		{
			get
			{
				SoapMessagesView View = null;
				
				if( TabControl.TabPages.Count > 0 )
				{
					TabPage SelectedTab = TabControl.SelectedTab;
					View = ( SoapMessagesView )SelectedTab.Controls[ 0 ];
				}
				else
				{
					View = SoapMessagesView;
				}
				
				return View;
			}
		}
		
		/// <summary>
		/// Gets or sets the active view's title.
		/// </summary>
		public string ActiveViewTitle
		{
			get
			{
				string Title = String.Empty;
				
				if( TabControl.TabPages.Count > 0 )
				{
					TabPage SelectedTab = TabControl.SelectedTab;
					Title = SelectedTab.Text;
				}
				else
				{
					Title = SoapMessagesView.Text;
				}
				
				return Title;
			}
			
			set
			{
				if( TabControl.TabPages.Count > 0 )
				{
					TabPage SelectedTab = TabControl.SelectedTab;
					SelectedTab.Text = value;
					SoapMessagesView View = ( SoapMessagesView )SelectedTab.Controls[ 0 ];
					View.Text = value;
				}
				else
				{
					SoapMessagesView.Text = value;
				}
			}
		}
		
		/// <summary>
		/// Gets the number of views.
		/// </summary>
		public int ViewCount
		{
			get
			{
				int Count = 1;
				
				if( TabControl.TabPages.Count > 0 )
				{
					Count = TabControl.TabPages.Count;
				}
				
				return Count;
			}
		}
		
		/// <summary>
		/// Adds a new view to the form.
		/// </summary>
		/// <param name="view">A new view.</param>
		/// <param name="title">The view's title.</param>
		public void AddView( SoapMessagesView view, string title )
		{
			if( ViewCount == 1 )
			{
				TabPage TabPage = new TabPage( SoapMessagesView.Text );
				TabPage.Controls.Add( SoapMessagesView );
				
				TabControl.TabPages.Add( TabPage );
				TabControl.SelectedTab = TabPage;
				
				Controls.Remove( SoapMessagesView );
				
				TabControl.Visible = true;
			}
			
			TabPage NewTabPage = new TabPage( title );
			
			view.Dock = DockStyle.Fill;
			view.Text = title;
			view.WordWrap = WordWrap;
			view.ListFont = ListFont;
			view.TextFont = TextFont;
			view.ListViewContextMenu = ListViewRightClickContextMenu;
			view.RequestTextBoxContextMenu = RightClickContextMenu;
			
			UpdateViewLayout( view );
			
			NewTabPage.Controls.Add( view );
			
			TabControl.TabPages.Add( NewTabPage );
			TabControl.SelectedTab = NewTabPage;
			
			SoapMessagesView = null;
		}
		
		/// <summary>
		/// Removes the active view.
		/// </summary>
		public void RemoveActiveView( )
		{
			CloseTab( TabControl.SelectedTab );
		}
		
		/// <summary>
		/// Removes the specified view.
		/// </summary>
		public void RemoveView( SoapMessagesView view )
		{
			TabPage TabPage = FindTabPage( view );
			if( TabPage != null )
			{
				CloseTab( TabPage );
			}
		}
		
		/// <summary>
		/// Redisplays the main form.  This is used to show the form when
		/// we are already running the soap trace application.
		/// </summary>
		public void Redisplay( )
		{
			if( InvokeRequired )
			{
				RedisplayInvoker Delegate = new RedisplayInvoker( Redisplay );
				Invoke( Delegate );
			}
			else
			{
				// If we are minimized then redisplay ourselves.
				
				if( WindowState == FormWindowState.Minimized )
				{
					WindowState = m_State;
				}
				
				// Bring the form to the front if it is not visible.
				
				if( Visible )
				{
					// Activate the main form.
					
					Activate( );
				}
				
				ShowInTaskbar = true;
				Visible = true;
			}
		}
		
		/// <summary>
		/// Gets or sets the caption of the main form.
		/// </summary>
		/// <remarks>
		/// <para>The caption of the title bar takes the form of:
		/// </para>
		/// <para>SoapTrace - Caption</para>
		/// </remarks>
		public string Caption
		{
			get
			{
				return m_Caption;
			}
			
			set
			{
				m_Caption = value;
				
				if( m_Caption.Length > 0 )
				{
					Text = String.Concat( Application.ProductName, " - ", m_Caption );
				}
				else
				{
					Text = Application.ProductName;
				}
			}
		}
		
		/// <summary>
		/// Gets the currently selected soap request and response item.
		/// </summary>
		public SoapRequestAndResponse SelectedItem
		{
			get
			{
				return ActiveView.SelectedItem;
			}
		}
		
		/// <summary>
		/// Clears all the items displayed on the form.
		/// </summary>
		public void Clear( )
		{
			ActiveView.Clear( );
		}
		
		/// <summary>
		/// Gets or sets the read-only flag on the soap request's
		/// text box.
		/// </summary>
		public bool RequestTextReadOnly
		{
			get
			{
				return ActiveView.RequestTextReadOnly;
			}
			
			set
			{
				ActiveView.RequestTextReadOnly = value;
			}
		}
		
		/// <summary>
		/// Adds the formatter menu items that are available.
		/// </summary>
		/// <param name="names">An array of formatter names to be
		/// added as menu items.</param>
		/// <remarks><para>These are the formatters in addition to the
		/// built-in defaults available in the View menu.</para>
		/// <para>
		///		<list type="bullet">
		///			<listheader>
		///				<term>Built-in Formatter</term>
		/// 			<description>Description</description>
		/// 		</listheader>
		/// 		<item>
		/// 			<term>No formatters selected.</term>
		/// 			<description>Displays the Http message body only.
		/// 			with no formatting applied.</description>
		/// 		</item>
		/// 		<item>
		/// 			<term>Http Headers</term>
		/// 			<description>Displays the Http message headers.
		/// 			</description>
		/// 		</item>
		/// 		<item>
		/// 			<term>Formatted message</term>
		/// 			<description>Formats the soap message xml on display by
		/// 			indenting the xml.
		/// 			</description>
		/// 		</item>
		/// 		<item>
		/// 			<term>Message parameters</term>
		/// 			<description>Extracts the soap message parameters and
		/// 			return values and displays them without any of the
		/// 			surrounding soap message xml.
		/// 			</description>
		/// 		</item>
		/// 	</list>
		/// </para>
		/// </remarks>
		public void AddFormatterMenuItems( string[ ] names )
		{
			foreach( string MenuItemName in names )
			{
				MenuItem MenuItem =
					new MenuItem( MenuItemName, new EventHandler( FormatterMenuItemClick ) );
				
				ViewMenuItem.MenuItems.Add( MenuItem );
			}
			
			// Make the separator visible if any message formatters were
			// added.
			if( names.Length > 0 )
			{
				ViewMenuItemSeparator2.Visible = true;
			}
		}
		
		/// <summary>
		/// Gets or sets the property which determines whether or not
		/// the soap message is displayed raw without standard xml indentation
		/// (i.e. pretty printed).
		/// </summary>
		public bool ViewRawSoapMessage
		{
			get
			{
				return ViewRawSoapMessageMenuItem.Checked;
			}
			
			set
			{
				ViewRawSoapMessageMenuItem.Checked = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the property which determines whether or not
		/// the soap message envelope is displayed.
		/// </summary>
		public bool ViewSoapEnvelope
		{
			get
			{
				return ViewSoapEnvelopeMenuItem.Checked;
			}
			
			set
			{
				ViewSoapEnvelopeMenuItem.Checked = value;
			}
		}
		
		
		/// <summary>
		/// Gets or sets the property which determines whether
		/// the soap message headers are displayed.
		/// </summary>
		public bool ViewHttpHeaders
		{
			get
			{
				return ViewHttpHeaderMenuItem.Checked;
			}
			
			set
			{
				ViewHttpHeaderMenuItem.Checked = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the flag indicating whether the soap message
		/// parameters are displayed without the rest of the soap message.
		/// </summary>
		public bool ViewSoapMessageParameters
		{
			get
			{
				return ViewSoapMessageParametersMenuItem.Checked;
			}
			
			set
			{
				ViewSoapMessageParametersMenuItem.Checked = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the current text word wrap flag.
		/// </summary>
		public bool WordWrap
		{
			get
			{
				return WordWrapMenuItem.Checked;
			}
			
			set
			{
				WordWrapMenuItem.Checked = value;
				
				ActiveView.WordWrap = value;
				
				foreach( TabPage TabPage in TabControl.TabPages )
				{
					SoapMessagesView View = ( SoapMessagesView )TabPage.Controls[ 0 ];
					View.WordWrap = value;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets if the application exists in the system tray.
		/// </summary>
		public bool ShowInTray
		{
			get
			{
				return m_ShowInTray;
			}
			
			set
			{
				if( m_ShowInTray != value )
				{
					m_ShowInTray = value;
					NotifyIcon.Visible = m_ShowInTray;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets the font to be used to display the soap message text.
		/// </summary>
		public Font TextFont
		{
			get
			{
				return ActiveView.TextFont;
			}
			
			set
			{
				ActiveView.TextFont = value;
				
				foreach( TabPage TabPage in TabControl.TabPages )
				{
					SoapMessagesView View = ( SoapMessagesView )TabPage.Controls[ 0 ];
					View.TextFont = value;
				}
			}
		}
		
		/// <summary>
		/// Gets or sets the font to use in the list view.
		/// </summary>
		public Font ListFont
		{
			get
			{
				return ActiveView.ListFont;
			}
			
			set
			{
				ActiveView.ListFont = value;
				
				foreach( TabPage TabPage in TabControl.TabPages )
				{
					SoapMessagesView View = ( SoapMessagesView )TabPage.Controls[ 0 ];
					View.ListFont = value;
				}
			}
		}
		
		/// <summary>
		/// Shows an error dialog.
		/// </summary>
		/// <param name="message">The error message to display.</param>
		public void ShowError( string message )
		{
			if( InvokeRequired )
			{
				ShowErrorInvoker Delegate = new ShowErrorInvoker( ShowError );
				Invoke( Delegate, new object[ ] { message } );
			}
			else
			{
				MessageBox.Show( message, Application.ProductName,
				                MessageBoxButtons.OK, MessageBoxIcon.Error );
			}
		}
		
		/// <summary>
		/// Checks or unchecks the formatter with the specified name in the
		/// View menu.  This is used to identify the currently active formatter.
		/// </summary>
		/// <param name="name">The name of the formatter.</param>
		/// <param name="itemChecked">A flag indicating whether to check or
		/// uncheck the menu item.</param>
		public void ViewFormatterChecked( string name, bool itemChecked )
		{
			foreach( MenuItem MenuItem in ViewMenuItem.MenuItems )
			{
				if( MenuItem.Text == name )
				{
					MenuItem.Checked = itemChecked;
				}
			}
		}
		
		/// <summary>
		/// Unchecks all currently checked formatter menu items.
		/// </summary>
		public void UncheckAllFormatters( )
		{
			for( int i = ViewMenuItemSeparator2.Index + 1;
			    i < ViewMenuItem.MenuItems.Count;
			    ++i )
			{
				MenuItem MenuItem = ViewMenuItem.MenuItems[ i ];
				MenuItem.Checked = false;
			}
		}
		
		/// <summary>
		/// Gets or sets whether the Tools->Debug Messages menu option
		/// is displayed.
		/// </summary>
		public bool ViewDebugMessages
		{
			get
			{
				return ToolsDebugMessagesMenuItem.Visible;
			}
			
			set
			{
				ToolsDebugMessagesMenuItem.Visible = value;
			}
		}
		
		/// <summary>
		/// Enables all the non-custom formatter menu items.
		/// </summary>
		public void EnableNonCustomFormatterMenuItems( )
		{
			EnableNonCustomFormatterMenuItems( true );
		}
		
		/// <summary>
		/// Disables all the non-custom formatter menu items.
		/// </summary>
		public void DisableNonCustomFormatterMenuItems( )
		{
			EnableNonCustomFormatterMenuItems( false );
		}
		
		/// <summary>
		/// Gets or sets the enabled state of the Close tab menu item.
		/// </summary>
		public bool FileCloseTabMenuItemEnabled
		{
			get
			{
				return FileCloseTabMenuItem.Enabled;
			}
			
			set
			{
				FileCloseTabMenuItem.Enabled = value;
			}
		}
		
		/// <summary>
		/// Gets or sets the enabled state of the Undo Close menu item.
		/// </summary>
		public bool UndoCloseMenuItemEnabled
		{
			get
			{
				return FileUndoCloseMenuItem.Enabled;
			}
			
			set
			{
				FileUndoCloseMenuItem.Enabled = value;
			}
		}
		
		/// <summary>
		/// Removes all submenu items from the undo close menu.
		/// </summary>
		public void ClearUndoCloseMenuItems( )
		{
			FileUndoCloseMenuItem.MenuItems.Clear( );
		}
		
		/// <summary>
		/// Adds a submenu item to the Undo close menu.
		/// </summary>
		public void AddUndoCloseMenuItem( string title )
		{
			MenuItem MenuItem = new MenuItem( title );
			MenuItem.Click += new EventHandler( UndoCloseSubmenuItemClick );
			FileUndoCloseMenuItem.MenuItems.Add( MenuItem );
		}
				
		/// <summary>
		/// Checks for the session being ended and shuts the application down.
		/// </summary>
		protected override void WndProc( ref Message m )
		{
			if ( m.Msg == WM_QUERYENDSESSION )
			{
				m_Close = true;
			}
			
			base.WndProc( ref m );
		}
		
		/// <summary>
		/// Closes down the application after the user clicks
		/// the File->Exit menu option.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FileExitMenuItemClick( object sender, EventArgs e )
		{
			m_Close = true;
			Close( );
		}
		
		/// <summary>
		/// Displays the currently active proxy server
		/// settings after the user selects the File->Settings menu option.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FileSettingsMenuItemClick( object sender, EventArgs e )
		{
			m_App.ChangeSettings( );
		}
		
		/// <summary>
		/// Clears the messages on display.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ViewClearMenuItemClick( object sender, EventArgs e )
		{
			m_App.Clear( );
		}
		
		/// <summary>
		/// The user has changed the word wrap setting.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void WordWrapMenuItemClick( object sender, EventArgs e )
		{
			WordWrapMenuItem.Checked = ! WordWrapMenuItem.Checked;
			m_App.SetWordWrap( WordWrapMenuItem.Checked );
		}
		
		/// <summary>
		/// The user wants to change the font being used to display the
		/// soap messages.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void TextFontMenuOptionClick( object sender, EventArgs e )
		{
			m_App.ChangeTextFont( );
		}
		
		/// <summary>
		/// The user wants to change the font being used to display the message
		/// list.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ListFontMenuItemClick(object sender, System.EventArgs e)
		{
			m_App.ChangeListFont( );
		}
		
		/// <summary>
		/// The user double clicked the tray icon.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void NotifyIconDoubleClick( object sender, System.EventArgs e )
		{
			Redisplay( );
		}
		
		/// <summary>
		/// The main form is closing.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void MainFormClosing( object sender, CancelEventArgs e )
		{
			if( m_Close || ( m_ShowInTray == false ) )
			{
				// We really are closing the app so shutdown.
				
				m_App.Shutdown( );
			}
			else
			{
				// Cancel the close and just minimise the form.
				
				e.Cancel = true;
				ShowInTaskbar = false;
				Visible = false;
				m_State = WindowState;
			}
		}
		
		/// <summary>
		/// Exits the application when the user selects the Exit menu option on the
		/// tray icon's menu.
		/// </summary>
		/// <param name="sender">The sender of the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void TrayExitMenuItemClick(object sender, System.EventArgs e)
		{
			m_Close = true;
			Close( );
		}
		
		/// <summary>
		/// Shows the main form when the user selects the tray icon's Show
		/// menu option.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void TrayShowMenuItemClick( object sender, EventArgs e )
		{
			Redisplay( );
		}
		
		/// <summary>
		/// Handles the View->Http Headers menu item click.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ViewHttpHeaderMenuItemClick( object sender, EventArgs e )
		{
			ViewHttpHeaderMenuItem.Checked = ! ViewHttpHeaderMenuItem.Checked;
			m_App.ViewHttpHeaders( ViewHttpHeaderMenuItem.Checked );
		}
		
		/// <summary>
		/// Handles the View->Raw Message menu item click.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ViewRawSoapMessageMenuItemClick( object sender, EventArgs e )
		{
			ViewRawSoapMessageMenuItem.Checked = ! ViewRawSoapMessageMenuItem.Checked;
			m_App.ViewRawSoapMessage( ViewRawSoapMessageMenuItem.Checked );
		}
		
		/// <summary>
		/// Handles the View->Message Parameters menu item click event.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ViewSoapMessageParametersMenuItemClick( object sender, EventArgs e )
		{
			ViewSoapMessageParametersMenuItem.Checked = ! ViewSoapMessageParametersMenuItem.Checked;
			m_App.ViewSoapMessageParameters( ViewSoapMessageParametersMenuItem.Checked );
		}
		
		/// <summary>
		/// Copies the currently selected text into the clipboard after
		/// the user selects the Copy menu option.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void CopyMenuItemClick( object sender, EventArgs e )
		{
			ActiveView.CopyText( );
		}
		
		/// <summary>
		/// Copies the currently selected text into the clipboard.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void RightClickCopyMenuItemClick( object sender, EventArgs e )
		{
			ActiveView.CopyText( );
		}
		
		/// <summary>
		/// Selects all the text in the currently active text box.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void RightClickSelectAllMenuItemClick( object sender, EventArgs e )
		{
			ActiveView.SelectAllText( );
		}
		
		/// <summary>
		/// Selects all the text in the currently active text box. User clicked the
		/// Edit Select All menu item.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void SelectAllMenuItemClick(object sender, System.EventArgs e)
		{
			ActiveView.SelectAllText( );
		}
		
		/// <summary>
		/// Changes the formatting being used to display the soap messages.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FormatterMenuItemClick( object sender, EventArgs e )
		{
			MenuItem MenuItem = ( MenuItem )sender;
			
			MenuItem.Checked = ! MenuItem.Checked;
			
			m_App.FormatterSelected( MenuItem.Text, MenuItem.Checked );
		}
		
		/// <summary>
		/// Opens up the find dialog allowing the user to search for
		/// text.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FindMenuItemClick( object sender, EventArgs e )
		{
			if( ActiveView.IsTextBoxActive )
			{
				string SearchText = String.Empty;
				
				if( ActiveView.SelectedText.Length > 0 )
				{
					// Put the currently highlighted text in the find dialog.
					
					SearchText = ActiveView.SelectedText;
				}
				else
				{
					// No text highlighted so use the last text the
					// user tried to find.
					
					SearchText = m_FindText;
				}
				
				if( m_FindTextForm != null )
				{
					// Set focus to dialog already on display.
					m_FindTextForm.Activate( );
				}
				else
				{
					// Show new dialog.
					ShowFindTextForm( SearchText, m_CaseSensitive );
				}
			}
		}
		
		/// <summary>
		/// Allows the user to find the next occurrence of the text they
		/// want to find.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FindNextMenuItemClick( object sender, EventArgs e )
		{
			if( m_FindText.Length == 0 )
			{
				FindMenuItemClick( sender, e );
			}
			else
			{
				FindTextFormFindNext( this, new FindNextEventArgs( m_FindText, m_CaseSensitive ) );
			}
		}
		
		/// <summary>
		/// Locates and highlights the next occurrence of the user specified
		/// search text in the active text box.
		/// </summary>
		/// <param name="sender">The event source</param>
		/// <param name="e">A <see cref="FindNextEventArgs"/>.</param>
		private void FindTextFormFindNext( object sender, FindNextEventArgs e )
		{
			if( ActiveView.IsTextBoxActive )
			{
				if( false == ActiveView.FindText( e.SearchText, e.CaseSensitive ) )
				{
					MessageBox.Show(
					                String.Concat( "Cannot find \"", e.SearchText, "\"" ),
					                Application.ProductName );
				}
			}
		}
		
		/// <summary>
		/// Handles the Tools->Options menu item clicked event.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void ToolsOptionsMenuItemClick( object sender, EventArgs e )
		{
			m_App.ShowToolsOptions( );
		}
		
		/// <summary>
		/// Saves the search settings after the find text form is closed.
		/// </summary>
		/// <param name="sender">The object that triggered the event.</param>
		/// <param name="e">The event's arguments.</param>
		private void FindTextFormClosed( object sender, EventArgs e )
		{
			// Save the search settings.
			
			FindTextForm FindTextForm = ( FindTextForm )sender;
			
			m_FindText = FindTextForm.FindText;
			m_CaseSensitive = FindTextForm.CaseSensitive;
			
			m_FindTextForm = null;
			
			FindTextForm.Dispose( );
		}
		
		/// <summary>
		/// Displays the find text form as a modeless dialog.
		/// </summary>
		/// <param name="searchText">The text to search for.</param>
		/// <param name="caseSensitive">A flag indicating whether
		/// the search is to be case sensitive.</param>
		private void ShowFindTextForm( string searchText, bool caseSensitive )
		{
			FindTextForm FindTextForm = new FindTextForm( );
			
			// Set up the find next callback.
			FindTextForm.FindNext += new FindTextForm.FindNextEventHandler(
			                                                               FindTextFormFindNext );
			
			// Set up the form closed callback.
			FindTextForm.Closed += new EventHandler( FindTextFormClosed );
			
			// Set the initial properties on the form.
			FindTextForm.CaseSensitive = caseSensitive;
			FindTextForm.FindText = searchText;
			
			// Set form's owner to be the main form otherwise
			// when we click away from the dialog it will be
			// hidden behind the main form.
			
			FindTextForm.Owner = this;
			
			// Set form's initial position since CenterParent property
			// does not seem to do anything form modeless dialogs.
			
			FindTextForm.StartPosition = FormStartPosition.Manual;
			FindTextForm.Top = Top + InitialFindTextFormTop;
			FindTextForm.Left = Left + InitialFindTextFormLeft;
			
			// Display the form.
			FindTextForm.Show( );
			
			// Save a reference to the currently displayed
			// modeless dialog so we can set focus to it
			// again if the user selects "Find" from the menu again.
			m_FindTextForm = FindTextForm;
		}
		
		/// <summary>
		/// Shows the About form.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void HelpAboutMenuItemClick( object sender, EventArgs e )
		{
			using( AboutForm Form = new AboutForm( ) )
			{
				Form.ShowDialog( );
			}
		}
		
		/// <summary>
		/// Shows/hides the soap message envelope.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void ViewSoapEnvelopeMenuItemClick(object sender, EventArgs e)
		{
			ViewSoapEnvelopeMenuItem.Checked = !ViewSoapEnvelopeMenuItem.Checked;
			
			m_App.ViewSoapEnvelope( ViewSoapEnvelopeMenuItem.Checked );
		}
		
		/// <summary>
		/// Shows the debug messages form.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void ToolsDebugMessagesMenuItemClick( object sender, EventArgs e )
		{
			m_App.ShowDebugMessages( );
		}
		
		/// <summary>
		/// Enables all the non-custom formatter menu items.
		/// </summary>
		/// <param name="enabled">Flag indicating whether the menu items
		/// should be enabled or disabled.</param>
		private void EnableNonCustomFormatterMenuItems( bool enabled )
		{
			ViewHttpHeaderMenuItem.Enabled = enabled;
			ViewRawSoapMessageMenuItem.Enabled = enabled;
			ViewSoapEnvelopeMenuItem.Enabled = enabled;
			ViewSoapMessageParametersMenuItem.Enabled = enabled;
		}
		
		/// <summary>
		/// Shows the packet viewer form.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void ToolsPacketViewerMenuItemClick( object sender, EventArgs e )
		{
			m_App.ShowPacketViewer( );
		}
		
		/// <summary>
		/// Gets the current configuration.
		/// </summary>
		private MainFormConfiguration GetConfiguration( )
		{
			MainFormConfiguration Config = new MainFormConfiguration( );
			Config.Location = m_CurrentLocation;
			Config.ClientSize = m_CurrentClientSize;
			Config.WordWrap = WordWrap;
			Config.ShowInTray = ShowInTray;
			
			// Text font settings.
			Config.TextFontName = TextFont.Name;
			Config.TextFontSize = TextFont.Size;
			Config.TextFontStyle = TextFont.Style;
			
			// List font settings.
			Config.ListFontName = ListFont.Name;
			Config.ListFontSize = ListFont.Size;
			Config.ListFontStyle = ListFont.Style;
			
			// Panel positions.
			Config.SoapMessagesPanelWidth = ActiveView.SoapMessagesPanelWidth;
			Config.MessageRequestPanelHeight = ActiveView.RequestPanelHeight;
			
			// View menu settings.
			Config.ViewHttpHeaders = ViewHttpHeaders;
			Config.ViewRawSoapMessage = ViewRawSoapMessage;
			Config.ViewSoapMessageParameters = ViewSoapMessageParameters;
			Config.ViewSoapEnvelope = ViewSoapEnvelope;
			
			// List view column widths.
			Config.TimeColumnHeaderWidth = ActiveView.TimeColumnHeaderWidth;
			Config.WebServiceOperationColumnHeaderWidth = ActiveView.WebServiceOperationColumnHeaderWidth;
			Config.WebServiceColumnHeaderWidth = ActiveView.WebServiceColumnHeaderWidth;
			Config.SourceColumnHeaderWidth = ActiveView.SourceColumnHeaderWidth;
			
			if( WindowState == FormWindowState.Minimized )
			{
				// Do not allow the main form to start up
				// minimized.
				Config.WindowState = FormWindowState.Normal;
			}
			else
			{
				Config.WindowState = WindowState;
			}
			
			return Config;
		}
		
		/// <summary>
		/// Updates the main form based on the configuration
		/// object.
		/// </summary>
		/// <param name="config">A main form configuration
		/// object.</param>
		private void UpdateMainForm( MainFormConfiguration config )
		{
			Location = config.Location;
			m_CurrentLocation = Location;
			
			ClientSize = config.ClientSize;
			m_CurrentClientSize = ClientSize;
			
			WordWrap = config.WordWrap;
			ShowInTray = config.ShowInTray;
			
			WindowState = config.WindowState;
			
			// View menu options.
			ViewHttpHeaders = config.ViewHttpHeaders;
			ViewRawSoapMessage = config.ViewRawSoapMessage;
			ViewSoapMessageParameters = config.ViewSoapMessageParameters;
			ViewSoapEnvelope = config.ViewSoapEnvelope;
			
			// Panel positions.
			SoapMessagesView.SoapMessagesPanelWidth = config.SoapMessagesPanelWidth;
			SoapMessagesView.RequestPanelHeight = config.MessageRequestPanelHeight;
			
			// List view column widths.
			SoapMessagesView.TimeColumnHeaderWidth = config.TimeColumnHeaderWidth;
			SoapMessagesView.WebServiceOperationColumnHeaderWidth = config.WebServiceOperationColumnHeaderWidth;
			SoapMessagesView.WebServiceColumnHeaderWidth = config.WebServiceColumnHeaderWidth;
			SoapMessagesView.SourceColumnHeaderWidth = config.SourceColumnHeaderWidth;
			
			try
			{
				TextFont = new Font( config.TextFontName, config.TextFontSize, config.TextFontStyle, GraphicsUnit.Point, ( ( Byte )( 0 ) ) );
				ListFont = new Font( config.ListFontName, config.ListFontSize, config.ListFontStyle, GraphicsUnit.Point, ( ( Byte )( 0 ) ) );
			}
			catch( Exception Ex )
			{
				// Ignore.
				Debug.WriteLine( String.Concat( "Failed to set the font. ", Ex.Message ) );
			}
		}
		
		/// <summary>
		/// Keeps track of the main form's size when it is not
		/// maximised or minimised.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void MainFormSizeChanged( object sender, EventArgs e )
		{
			if( WindowState == FormWindowState.Normal )
			{
				m_CurrentClientSize = ClientSize;
			}
		}
		
		/// <summary>
		/// Keeps track of the main form's location when it is not
		/// maximised or minimised.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void MainFormLocationChanged( object sender, EventArgs e )
		{
			if( WindowState == FormWindowState.Normal )
			{
				m_CurrentLocation = Location;
			}
		}
		
		/// <summary>
		/// Replay the selected message.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void ListViewRightClickReplayMenuItemClick( object sender, EventArgs e )
		{
			m_App.Replay( );
		}
		
		/// <summary>
		/// Replay the selected message.
		/// </summary>
		/// <param name="sender">The event source.</param>
		/// <param name="e">The event arguments.</param>
		private void ToolsReplayMenuItemClick( object sender, EventArgs e )
		{
			m_App.Replay( );
		}
		
		/// <summary>
		/// Opens a new tab.
		/// </summary>
		private void FileNewTabMenuItemClick( object sender, EventArgs e )
		{
			m_App.NewTab( );
		}
		
		/// <summary>
		/// Closes the currently active tab.
		/// </summary>
		private void FileCloseTabMenuItemClick( object sender, EventArgs e )
		{
			m_App.CloseTab( );
		}
		
		/// <summary>
		/// The selected tab has been changed.
		/// </summary>
		private void TabControlSelectedIndexChanged( object sender, EventArgs e )
		{
			// Make sure the new view has the same layout as the
			// previously selected one.
			UpdateViewLayout( ActiveView );
			
			// Set the previously active view.
			m_PreviouslyActiveView = ActiveView;
			
			// Inform the app.
			m_App.TabChanged( );
		}
		
		/// <summary>
		/// Updates the active view so its layout matches the
		/// previously selected view.
		/// </summary>
		private void UpdateViewLayout( SoapMessagesView view )
		{
			if( m_PreviouslyActiveView != null )
			{
				// Panel positions.
				view.SoapMessagesPanelWidth = m_PreviouslyActiveView.SoapMessagesPanelWidth;
				view.RequestPanelHeight = m_PreviouslyActiveView.RequestPanelHeight;
				
				// List view column widths.
				view.TimeColumnHeaderWidth = m_PreviouslyActiveView.TimeColumnHeaderWidth;
				view.WebServiceOperationColumnHeaderWidth = m_PreviouslyActiveView.WebServiceOperationColumnHeaderWidth;
				view.WebServiceColumnHeaderWidth = m_PreviouslyActiveView.WebServiceColumnHeaderWidth;
				view.SourceColumnHeaderWidth = m_PreviouslyActiveView.SourceColumnHeaderWidth;
			}
		}
		
		/// <summary>
		/// Closes the tab when the user middle clicks it.
		/// </summary>
		void TabControlMouseUp( object sender, MouseEventArgs e )
		{
			// Reset right clicked tab page.
			RightClickedTabPage = null;
			
			if( e.Button == MouseButtons.Middle )
			{
				TabPage TabPage = GetTabPageClicked( e.X, e.Y );
				if( TabPage != null )
				{
					SoapMessagesView View = ( SoapMessagesView )TabPage.Controls[ 0 ];
					m_App.CloseTab( View );
				}
			}
			else if ( e.Button == MouseButtons.Right )
			{
				RightClickedTabPage = GetTabPageClicked( e.X, e.Y );
			}
		}
		
		/// <summary>
		/// Determines the tab page that was clicked based on the click
		/// on the tab.
		/// </summary>
		TabPage GetTabPageClicked( int x, int y )
		{
			TabPage TabPage = null;
			
			for( int i = 0; i < TabControl.TabPages.Count; ++i )
			{
				Rectangle TabRect = TabControl.GetTabRect( i );
				if( TabRect.Contains( x, y ) )
				{
					TabPage = TabControl.TabPages[ i ];
					break;
				}
			}
			
			return TabPage;
		}
		
		/// <summary>
		/// Closes the specified tab.
		/// </summary>
		void CloseTab( TabPage tabPage )
		{
			if( ViewCount > 1 )
			{
				TabControl.TabPages.Remove( tabPage );
				tabPage.Dispose( );
				
				if( TabControl.TabPages.Count == 1 )
				{
					TabPage TabPage = TabControl.SelectedTab;
					
					SoapMessagesView = ( SoapMessagesView )TabPage.Controls[ 0 ];
					Controls.Add( SoapMessagesView );
					
					TabControl.TabPages.Remove( TabPage );
					TabPage.Dispose( );
					
					TabControl.Visible = false;
				}
			}
		}
		
		/// <summary>
		/// Finds the tab page containing the specified view.
		/// </summary>
		TabPage FindTabPage( SoapMessagesView view )
		{
			TabPage MatchedTabPage = null;
			
			foreach( TabPage TabPage in TabControl.TabPages )
			{
				SoapMessagesView MatchedView = ( SoapMessagesView )TabPage.Controls[ 0 ];
				
				if( Object.ReferenceEquals( MatchedView, view ) )
				{
					MatchedTabPage = TabPage;
					break;
				}
			}
			
			return MatchedTabPage;
		}
		
		/// <summary>
		/// Closes the tab that the user right clicked.
		/// </summary>
		void TabControlCloseTabMenuItemClick( object sender, EventArgs e )
		{
			// Close the tab that was right clicked.
			
			if( RightClickedTabPage != null )
			{
				SoapMessagesView View = ( SoapMessagesView )RightClickedTabPage.Controls[ 0 ];
				m_App.CloseTab( View );
			}
		}
		
		/// <summary>
		/// Re-opens the tab that was closed.
		/// </summary>
		void UndoCloseSubmenuItemClick( object sender, EventArgs e )
		{
			MenuItem MenuItem = ( MenuItem )sender;
			m_App.UndoCloseTab( MenuItem.Index );
		}
	}
}
